/*!
* \file WtSelTicker.cpp
* \project	WonderTrader
*
* \author Wesley
* \date 2020/03/30
*
* \brief SEL实时行情处理器实现文件
* 
* 本文件实现了SEL实时行情处理器的核心功能，主要包括：
* 1. 选股策略实时行情数据的接收和转发处理
* 2. 基于交易时段的时间驱动机制
* 3. 分钟线闭合事件的精确触发
* 4. 主力合约和次主力合约的自动处理
* 5. 多线程时间检查和事件调度
* 6. 与数据存储模块的协调工作
* 
* 该实现专门为选股策略场景设计，采用了稳定的时间控制
* 和多线程并发处理，确保选股策略能够获得准确的市场数据。
*/
#include "WtSelTicker.h"
#include "WtSelEngine.h"
#include "../Includes/IDataReader.h"

#include "../Share/TimeUtils.hpp"
#include "../Includes/WTSSessionInfo.hpp"
#include "../Includes/IBaseDataMgr.h"
#include "../Includes/IHotMgr.h"
#include "../Share/CodeHelper.hpp"

#include "../WTSTools/WTSLogger.h"

USING_NS_WTP;

/**
 * @brief 构造函数
 * @param engine SEL引擎指针
 * 
 * 初始化SEL实时行情处理器的各个成员变量，
 * 设置初始状态为未启动状态
 */
WtSelRtTicker::WtSelRtTicker(WtSelEngine* engine)
	: _engine(engine)
	, _stopped(false)
	, _date(0)
	, _time(UINT_MAX)
	, _next_check_time(0)
	, _last_emit_pos(0)
	, _cur_pos(0)
{
}

/**
 * @brief 析构函数
 * 
 * 清理资源，确保线程正常退出
 */
WtSelRtTicker::~WtSelRtTicker()
{
}

/**
 * @brief 初始化行情处理器
 * @param store 数据存储接口指针
 * @param sessionID 交易时段ID
 * 
 * 设置数据存储接口和交易时段信息，
 * 获取当前系统时间作为初始时间
 */
void WtSelRtTicker::init(IDataReader* store, const char* sessionID)
{
	_store = store;
	_s_info = _engine->get_session_info(sessionID);

	TimeUtils::getDateTime(_date, _time);
}

/**
 * @brief 触发价格事件
 * @param curTick 当前tick数据
 * @param hotFlag 热点标志
 * 
 * 将tick数据转发给SEL引擎进行处理，
 * 同时处理主力合约和次主力合约的数据转发
 */
void WtSelRtTicker::trigger_price(WTSTickData* curTick, uint32_t hotFlag /* = 0 */)
{
	if (_engine)
	{
		std::string stdCode = curTick->code();
		_engine->on_tick(stdCode.c_str(), curTick);

		if (hotFlag != 0)
		{
			WTSTickData* hotTick = WTSTickData::create(curTick->getTickStruct());
			std::string hotCode = (hotFlag == 1) ? CodeHelper::stdCodeToStdHotCode(stdCode.c_str()) : CodeHelper::stdCodeToStd2ndCode(stdCode.c_str());
			hotTick->setCode(hotCode.c_str(), hotCode.size());
			_engine->on_tick(hotCode.c_str(), hotTick);
			hotTick->release();
		}
	}
}

/**
 * @brief 处理实时行情数据
 * @param curTick 当前tick数据
 * @param hotFlag 热点标志
 * 
 * 接收实时tick数据，进行时间驱动处理：
 * 1. 检查数据时间的有效性
 * 2. 更新当前时间和分钟位置
 * 3. 判断是否需要触发分钟线闭合事件
 * 4. 转发tick数据给引擎
 * 5. 更新引擎的时间状态
 */
void WtSelRtTicker::on_tick(WTSTickData* curTick, uint32_t hotFlag /* = 0 */)
{
	if (_thrd == NULL)
	{
		trigger_price(curTick, hotFlag);
		return;
	}

	uint32_t uDate = curTick->actiondate();
	uint32_t uTime = curTick->actiontime();

	if (_date != 0 && (uDate < _date || (uDate == _date && uTime < _time)))
	{
		//WTSLogger::info("数据时间%u小于本地时间%u", uTime, _time);
		trigger_price(curTick, hotFlag);
		return;
	}

	_date = uDate;
	_time = uTime;

	uint32_t curMin = _time / 100000;
	uint32_t curSec = _time % 100000;
	uint32_t minutes = _s_info->timeToMinutes(curMin);
	bool isSecEnd = _s_info->isLastOfSection(curMin);
	if (isSecEnd)
	{
		minutes--;
	}
	minutes++;
	uint32_t rawMin = curMin;
	curMin = _s_info->minuteToTime(minutes);

	if (_cur_pos == 0)
	{
		//如果当前时间是0, 则直接赋值即可
		_cur_pos = minutes;
	}
	else if (_cur_pos < minutes)
	{
		//如果已记录的分钟小于新的分钟, 说明要触发闭合事件
		//这时候要先触发闭合, 再修改平台时间和价格
		if (_last_emit_pos < _cur_pos)
		{
			//触发数据回放模块
			StdUniqueLock lock(_mtx);

			//先更新时间戳
			_last_emit_pos = _cur_pos;

			uint32_t thisMin = _s_info->minuteToTime(_cur_pos);

			WTSLogger::info("Minute Bar %u.%04u Closed by data", _date, thisMin);
			if (_store)
				_store->onMinuteEnd(_date, thisMin);

			_engine->on_minute_end(_date, thisMin);

			uint32_t offMin = _s_info->offsetTime(thisMin, true);
			if (offMin == _s_info->getCloseTime(true))
			{
				_engine->on_session_end();
			}
		}

		trigger_price(curTick, hotFlag);
		if (_engine)
		{
			_engine->set_date_time(_date, curMin, curSec, rawMin);
			_engine->set_trading_date(curTick->tradingdate());
		}

		_cur_pos = minutes;
	}
	else
	{
		//如果还是在同一分钟的, 则直接触发价格和时间事件
		trigger_price(curTick, hotFlag);
		if (_engine)
			_engine->set_date_time(_date, curMin, curSec, rawMin);
	}

	uint32_t sec = curSec / 1000;
	uint32_t msec = curSec % 1000;
	uint32_t left_ticks = (60 - sec) * 1000 - msec;
	_next_check_time = TimeUtils::getLocalTimeNow() + left_ticks;
}

/**
 * @brief 启动行情处理器
 * 
 * 启动SEL行情处理器的主要流程：
 * 1. 初始化SEL引擎
 * 2. 计算当前交易日
 * 3. 触发交易时段开始事件
 * 4. 启动独立的时间检查线程
 * 
 * 时间检查线程负责在没有行情数据时自动触发分钟线闭合事件，
 * 确保选股策略能够按时接收到K线闭合通知
 */
void WtSelRtTicker::run()
{
	if (_thrd)
		return;

	_engine->on_init();

	uint32_t curTDate = _engine->get_basedata_mgr()->calcTradingDate(_s_info->id(), _engine->get_date(), _engine->get_min_time(), true);
	_engine->set_trading_date(curTDate);

	_engine->on_session_begin();

	//先检查当前时间, 如果时间未到则等待
	//uint32_t offTime = _s_info->offsetTime(_engine->get_min_time());

	_thrd.reset(new StdThread([this](){
		while (!_stopped)
		{
			if (_time != UINT_MAX && _s_info->isInTradingTime(_time / 100000, true))
			{
				std::this_thread::sleep_for(std::chrono::milliseconds(10));
				uint64_t now = TimeUtils::getLocalTimeNow();

				if (now >= _next_check_time && _last_emit_pos < _cur_pos)
				{
					//触发数据回放模块
					StdUniqueLock lock(_mtx);

					//先更新时间戳
					_last_emit_pos = _cur_pos;

					uint32_t thisMin = _s_info->minuteToTime(_cur_pos);
					_time = thisMin;

					//如果thisMin为0, 说明是前一天的最后一分钟
					//或者是跨夜交易时段的话, 说明日期实际是第二天, 要自动+1
					//同时为时间序列235959xxx, 这里也要手动转为0
					if (thisMin == 0)
					{
						uint32_t lastDate = _date;
						_date = TimeUtils::getNextDate(_date);
						_time = 0;
						WTSLogger::info("Data automatically changed at time 00:00: %u -> %u", lastDate, _date);
					}

					WTSLogger::info("Minute bar %u.%04u closed automatically", _date, thisMin);
					if (_store)
						_store->onMinuteEnd(_date, thisMin);

					_engine->on_minute_end(_date, thisMin);

					uint32_t offMin = _s_info->offsetTime(thisMin, true);
					if (offMin == _s_info->getCloseTime(true))
					{
						_engine->on_session_end();
					}

					if (_engine)
						_engine->set_date_time(_date, thisMin, 0);
				}
			}
			else
			{
				std::this_thread::sleep_for(std::chrono::seconds(1));
			}
		}
	}));
}

/**
 * @brief 停止行情处理器
 * 
 * 设置停止标志并等待时间检查线程退出，
 * 确保所有资源得到正确清理
 */
void WtSelRtTicker::stop()
{
	_stopped = true;
	if (_thrd)
		_thrd->join();
}
